#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
下载相关API路由
"""

import os
import asyncio
import threading
import concurrent.futures
from fastapi import APIRouter, HTTPException
from .models import DownloadRequest, CookieRequest, KuaishouBatchRequest

# 全局任务管理
active_tasks = {}
task_stop_flags = {}

# 导入下载相关模块
import sys
from pathlib import Path
current_dir = Path(__file__).parent
parent_dir = current_dir.parent.parent
sys.path.insert(0, str(parent_dir))

try:
    from downloader_core import DownloaderCore
    from vmq_downloader import WeimiquanDownloader
    from tiefenquan_downloader import TiefenquanDownloader
except ImportError:
    # 创建一个默认的下载器实例
    class DownloaderCore:
        def get_video_url_and_download(self, url, save_path, progress_callback, log_callback):
            log_callback("模拟抖音视频下载...")

        def get_douyin_img(self, url, save_path, progress_callback, log_callback):
            log_callback("模拟抖音图片下载...")

        def download_kuaishou_video(self, url, save_path, progress_callback, log_callback):
            log_callback("模拟快手视频下载...")

        def parse_and_set_cookies(self, cookies, platform):
            return 1

    class WeimiquanDownloader:
        def __init__(self):
            pass
        def download_images(self, url, save_path):
            pass


# 快手批量下载器类
class KuaishouBatchDownloader:
    """快手批量下载器 - Web版本"""

    def __init__(self, user_id, save_path, max_workers=5):
        """
        初始化快手批量下载器

        Args:
            user_id (str): 快手用户ID
            save_path (str): 保存路径
            max_workers (int): 最大并发下载数
        """
        self.user_id = user_id
        self.save_path = save_path
        self.max_workers = max_workers
        self.author_name = None
        self.download_path = None
        self.session = None
        self.payload = None

    def _setup_session(self):
        """设置session的请求头和Cookie"""
        import requests
        self.session = requests.Session()

        # 设置User-Agent
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
            "Content-Type": "application/json",
            "Referer": f"https://kuaishou.cn/profile/{self.user_id}",
        })

        # 设置Cookie
        cookies = {
            "kpf": "PC_WEB",
            "clientid": "3",
            "did": "web_cabb2ac62d58ddcb1c807794a5c5a043",
            "userId": "254593641",
            "kuaishou.server.webday7_st": "ChprdWFpc2hvdS5zZXJ2ZXIud2ViZGF5Ny5zdBKwAVDJGlAKJdL3MIuIoUaDM_S6oJt0_t8Dk28-KS7mXHIFzXxoQHsjLp7jpxTXOQi59PcTVqfGUlAM7OeW6ySUz4rl_Yuz4UL_40Q7JipCmNTkSuwKr6p20DA068NpRuTd4FCzej3Ml12SRnZJmRh7Soht0nunm4MljYjrkX7faKtbsIB5U5pFigvcpQl6tofD_Osl0qOBDTs2Xw3aWgmjaSXlfhiucu36RhLrf4euxIHgGhJglXWxx8RvW8kJJMTzKPTd1I8iICtsKJvZFJMy3I45clWXTOkh_p6fcNVOw5QBBNpRDWipKAUwAQ",
            "kuaishou.server.webday7_ph": "54fef7242fe0dd4bde4aac451c27c7ced4b7",
            "kpn": "KUAISHOU_VISION",
        }
        self.session.cookies.update(cookies)

        # 设置GraphQL查询载荷
        self.payload = {
            "operationName": "visionProfilePhotoList",
            "variables": {"userId": self.user_id, "pcursor": "", "page": "profile"},
            "query": """
                fragment photoContent on PhotoEntity {
                    __typename
                    id
                    duration
                    caption
                    originCaption
                    likeCount
                    viewCount
                    commentCount
                    realLikeCount
                    coverUrl
                    photoUrl
                    photoH265Url
                    manifest
                    manifestH265
                    videoResource
                    coverUrls {
                        url
                        __typename
                    }
                    timestamp
                    expTag
                    animatedCoverUrl
                    distance
                    videoRatio
                    liked
                    stereoType
                    profileUserTopPhoto
                    musicBlocked
                    riskTagContent
                    riskTagUrl
                }
                fragment recoPhotoFragment on recoPhotoEntity {
                    __typename
                    id
                    duration
                    caption
                    originCaption
                    likeCount
                    viewCount
                    commentCount
                    realLikeCount
                    coverUrl
                    photoUrl
                    photoH265Url
                    manifest
                    manifestH265
                    videoResource
                    coverUrls {
                        url
                        __typename
                    }
                    timestamp
                    expTag
                    animatedCoverUrl
                    distance
                    videoRatio
                    liked
                    stereoType
                    profileUserTopPhoto
                    musicBlocked
                    riskTagContent
                    riskTagUrl
                }
                fragment feedContentWithLiveInfo on Feed {
                    type
                    author {
                        id
                        name
                        headerUrl
                        following
                        livingInfo
                        headerUrls {
                            url
                            __typename
                        }
                        __typename
                    }
                    photo {
                        ...photoContent
                        ...recoPhotoFragment
                        __typename
                    }
                    canAddComment
                    llsid
                    status
                    currentPcursor
                    tags {
                        type
                        name
                        __typename
                    }
                    __typename
                }
                query visionProfilePhotoList($pcursor: String, $userId: String, $page: String, $webPageArea: String) {
                    visionProfilePhotoList(pcursor: $pcursor, userId: $userId, page: $page, webPageArea: $webPageArea) {
                        result
                        llsid
                        webPageArea
                        feeds {
                            ...feedContentWithLiveInfo
                            __typename
                        }
                        hostName
                        pcursor
                        __typename
                    }
                }
            """,
        }

    def _sanitize_filename(self, filename):
        """清理文件名，移除非法字符"""
        import re
        if not filename:
            return ""

        # 移除换行符和制表符
        filename = filename.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ')

        # 移除或替换Windows非法字符
        illegal_chars = r'[<>:"/\\|?*]'
        filename = re.sub(illegal_chars, "_", filename)

        # 移除控制字符
        filename = re.sub(r'[\x00-\x1f\x7f-\x9f]', "", filename)

        # 替换多个连续空格为单个空格
        filename = re.sub(r'\s+', ' ', filename)

        # 移除开头和结尾的空格和点号
        filename = filename.strip(' .')

        # 限制长度（Windows路径限制）
        if len(filename) > 100:
            filename = filename[:100].strip(' .')

        # 如果清理后为空，返回默认名称
        if not filename:
            filename = "untitled"

        return filename

    def get_video_list(self, log_callback=None, stop_flag_callback=None):
        """获取用户的所有视频列表"""
        import time
        import json

        videos = []
        pcursor = ""
        page_count = 0

        if log_callback:
            log_callback(f"开始获取用户 {self.user_id} 的视频列表...")

        try:
            while pcursor != "no_more":
                if stop_flag_callback and stop_flag_callback():
                    if log_callback:
                        log_callback("用户中断获取视频列表")
                    break

                try:
                    # 复制载荷并设置游标
                    current_payload = self.payload.copy()
                    current_payload["variables"]["pcursor"] = pcursor

                    # 发送请求
                    response = self.session.post("https://kuaishou.cn/graphql", json=current_payload)
                    response.raise_for_status()
                    data = response.json()

                    # 解析响应数据
                    vision_data = data.get("data", {}).get("visionProfilePhotoList", {})
                    feeds = vision_data.get("feeds", [])

                    # 提取作者信息（第一次获取时）
                    if self.author_name is None and feeds:
                        first_feed = feeds[0]
                        author = first_feed.get("author", {})
                        if author:
                            self.author_name = author.get("name", f"user_{self.user_id}")
                            # 清理作者名，用作文件夹名
                            safe_author_name = self._sanitize_filename(self.author_name)
                            self.download_path = os.path.join(self.save_path, safe_author_name)
                            # 创建作者文件夹
                            os.makedirs(self.download_path, exist_ok=True)
                            if log_callback:
                                log_callback(f"作者: {self.author_name}")
                                log_callback(f"下载路径: {self.download_path}")

                    # 提取视频信息
                    for feed in feeds:
                        photo = feed.get("photo", {})
                        if photo:
                            video_info = {
                                "id": photo.get("id", ""),
                                "url": photo.get("photoUrl", ""),
                                "caption": photo.get("caption", ""),
                                "duration": photo.get("duration", 0),
                                "likeCount": photo.get("likeCount", 0),
                                "viewCount": photo.get("viewCount", 0),
                                "timestamp": photo.get("timestamp", 0),
                            }
                            if video_info["id"] and video_info["url"]:
                                videos.append(video_info)

                    # 获取下一页游标
                    pcursor = vision_data.get("pcursor", "no_more")
                    page_count += 1

                    if log_callback:
                        log_callback(f"第 {page_count} 页: 获取到 {len(feeds)} 个视频，总计 {len(videos)} 个")

                    # 避免请求过快
                    time.sleep(0.5)

                except Exception as e:
                    if log_callback:
                        log_callback(f"获取视频列表时出错: {e}")
                    break

        except Exception as e:
            if log_callback:
                log_callback(f"获取视频列表异常: {e}")

        if log_callback:
            log_callback(f"视频列表获取完成，共找到 {len(videos)} 个视频")
        return videos

    def download_video(self, video_info, log_callback=None, stop_flag_callback=None):
        """下载单个视频"""
        video_id = video_info["id"]
        video_url = video_info["url"]
        caption = video_info.get("caption", "")

        try:
            # 构造文件名
            safe_caption = self._sanitize_filename(caption)
            if safe_caption and safe_caption != "untitled":
                filename = f"{video_id}_{safe_caption}.mp4"
            else:
                filename = f"{video_id}.mp4"

            # 再次验证文件名长度，确保完整路径不会太长
            max_filename_length = 200  # 保守估计
            if len(filename) > max_filename_length:
                # 截断标题部分，保留视频ID
                max_caption_length = max_filename_length - len(video_id) - 10  # 10是"_"和".mp4"的长度
                if max_caption_length > 0:
                    safe_caption = safe_caption[:max_caption_length].strip(' .')
                    filename = f"{video_id}_{safe_caption}.mp4"
                else:
                    filename = f"{video_id}.mp4"

            filepath = os.path.join(self.download_path, filename)

            # 验证完整路径长度（Windows限制260字符）
            if len(filepath) > 250:
                # 使用更短的文件名
                filename = f"{video_id}.mp4"
                filepath = os.path.join(self.download_path, filename)

            # 检查文件是否已存在
            if os.path.exists(filepath):
                if log_callback:
                    log_callback(f"⚠ 文件已存在，跳过: {filename}")
                return True

            # 下载视频
            if log_callback:
                log_callback(f"📥 开始下载: {filename}")
            response = self.session.get(video_url, stream=True, timeout=30)
            response.raise_for_status()

            # 获取文件大小
            total_size = int(response.headers.get("content-length", 0))
            downloaded_size = 0

            # 创建临时文件，下载完成后重命名
            temp_filepath = filepath + ".tmp"

            with open(temp_filepath, "wb") as f:
                for chunk in response.iter_content(chunk_size=8192):
                    if stop_flag_callback and stop_flag_callback():
                        # 如果用户请求停止，删除临时文件并返回
                        f.close()
                        if os.path.exists(temp_filepath):
                            os.remove(temp_filepath)
                        return False

                    if chunk:
                        f.write(chunk)
                        downloaded_size += len(chunk)

            # 验证下载完整性
            if total_size > 0 and downloaded_size != total_size:
                if log_callback:
                    log_callback(f"⚠ 下载不完整: {filename} ({downloaded_size}/{total_size} bytes)")
                if os.path.exists(temp_filepath):
                    os.remove(temp_filepath)
                return False

            # 重命名临时文件为最终文件
            if os.path.exists(temp_filepath):
                os.rename(temp_filepath, filepath)

            if log_callback:
                log_callback(f"✅ 下载完成: {filename} ({downloaded_size} bytes)")
            return True

        except Exception as e:
            if log_callback:
                log_callback(f"❌ 下载失败 {video_id}: {e}")
            # 清理可能的不完整文件
            try:
                if "filepath" in locals() and os.path.exists(filepath):
                    os.remove(filepath)
                if "temp_filepath" in locals() and os.path.exists(temp_filepath):
                    os.remove(temp_filepath)
            except:
                pass
            return False

    def download_all(self, log_callback=None, progress_callback=None, stop_flag_callback=None):
        """执行批量下载"""
        try:
            if log_callback:
                log_callback("=" * 50)
                log_callback(f"开始快手批量下载任务 - 用户ID: {self.user_id}")
                log_callback("=" * 50)

            # 创建基础下载目录
            os.makedirs(self.save_path, exist_ok=True)

            # 初始化session
            self._setup_session()

            # 获取视频列表
            if log_callback:
                log_callback("正在获取用户视频列表...")
            if progress_callback:
                progress_callback(10)

            videos = self.get_video_list(log_callback, stop_flag_callback)

            if stop_flag_callback and stop_flag_callback():
                if log_callback:
                    log_callback("用户取消下载")
                return False

            if not videos:
                if log_callback:
                    log_callback("未找到任何视频，请检查用户ID是否正确")
                return False

            if not self.download_path:
                if log_callback:
                    log_callback("未能获取作者信息，无法创建下载目录")
                return False

            if log_callback:
                log_callback(f"\n开始下载 {len(videos)} 个视频...")

            # 并发下载
            success_count = 0
            failed_count = 0

            with concurrent.futures.ThreadPoolExecutor(max_workers=self.max_workers) as executor:
                # 提交所有下载任务
                future_to_video = {
                    executor.submit(self.download_video, video, log_callback, stop_flag_callback): video
                    for video in videos
                }

                # 处理完成的任务
                try:
                    for i, future in enumerate(concurrent.futures.as_completed(future_to_video)):
                        if stop_flag_callback and stop_flag_callback():
                            if log_callback:
                                log_callback("用户中断下载，正在停止...")
                            # 取消未完成的任务
                            for f in future_to_video:
                                f.cancel()
                            break

                        video = future_to_video[future]
                        try:
                            if future.result():
                                success_count += 1
                            else:
                                failed_count += 1
                        except Exception as e:
                            if log_callback:
                                log_callback(f"❌ 任务执行异常 {video['id']}: {e}")
                            failed_count += 1

                        # 更新进度
                        if progress_callback:
                            progress = int((i + 1) / len(videos) * 90) + 10  # 10-100%
                            progress_callback(progress)

                except Exception as e:
                    if log_callback:
                        log_callback(f"下载过程中出现异常: {e}")

            # 输出统计信息
            if log_callback:
                log_callback("\n" + "=" * 50)
                log_callback(f"下载完成！")
                log_callback(f"成功: {success_count}")
                log_callback(f"失败: {failed_count}")
                log_callback(f"总计: {len(videos)}")
                if len(videos) > 0:
                    log_callback(f"成功率: {success_count/len(videos)*100:.1f}%")
                log_callback("=" * 50)

            return success_count > 0

        except Exception as e:
            if log_callback:
                log_callback(f"批量下载异常: {e}")
            return False
    
    class TiefenquanDownloader:
        def __init__(self):
            pass
        def download_images(self, url, save_path):
            pass
    
    # 创建模拟的连接管理器
    class MockManager:
        async def send_message(self, client_id, message):
            pass  # 静默处理消息发送
    
    manager = MockManager()

router = APIRouter()
downloader_core = DownloaderCore()

def download_single_vmq_page(page_url, save_dir, log_callback, task_id=None):
    """下载单个微密圈页面的所有图片"""
    try:
        import requests
        import re
        import os
        from concurrent.futures import ThreadPoolExecutor, as_completed
        
        # 使用更完整的请求头和Cookie
        headers = {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
            "Accept-Language": "zh-CN,zh;q=0.9,ja;q=0.8",
            "Cache-Control": "max-age=0",
            "Connection": "keep-alive",
            "Referer": "https://wmqxz.net/",
            "Sec-Fetch-Dest": "document",
            "Sec-Fetch-Mode": "navigate",
            "Sec-Fetch-Site": "same-origin",
            "Sec-Fetch-User": "?1",
            "Upgrade-Insecure-Requests": "1",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
        }
        
        # 使用微密圈的Cookie（从vmq_batch.py复制）
        cookies = {
            '__51uvsct__KisFAjMmWonEmmaa': '1',
            '__51vcke__KisFAjMmWonEmmaa': '124af7b9-a489-54e1-9e9f-2aad28de65e5',
            '__51vuft__KisFAjMmWonEmmaa': '1747203033573',
            '__51vcke__KpRFHkppObUqVbFN': 'af7f4762-a1c2-542c-b776-2e8c1f96f3bb',
            '__51vuft__KpRFHkppObUqVbFN': '1753076527969',
            'deviceId': '5Bnzbb6FGhh3eXhdNbbjGaHQ',
            'userStore': '%7B%22info%22%3A%7B%22userId%22%3A%221947474922350616576%22%2C%22merchantAcct%22%3A%22sf555%22%2C%22masterAcct%22%3A%22sf555_m%22%2C%22agentAcct%22%3A%22sf555_m_no_agent%22%2C%22userAcct%22%3A%226CJB9M69%22%2C%22acctType%22%3A3%2C%22referCode%22%3Anull%2C%22shareCode%22%3A%226CJB9M69%22%2C%22isPartner%22%3A0%2C%22phoneNumber%22%3Anull%2C%22background%22%3Anull%2C%22headUrl%22%3A%22%2Fadmin%2Fuser%2Fhead%2Fb1f9ce42dc8341c587a9f9ff5c28e887%22%2C%22nickName%22%3A%226CJB9M69%22%2C%22signature%22%3Anull%2C%22loginType%22%3Anull%2C%22coinBalance%22%3A0%2C%22balance%22%3A0%2C%22exp%22%3A0%2C%22expLevel%22%3A0%2C%22iconFree%22%3A1%2C%22vipBegin%22%3Anull%2C%22vipEnd%22%3Anull%2C%22vipFlag%22%3Afalse%2C%22vipTitle%22%3Anull%2C%22vipPackageId%22%3Anull%2C%22userStatus%22%3A0%2C%22followers%22%3Anull%2C%22followed%22%3Anull%2C%22lastLoginDate%22%3A%222025-07-22%22%2C%22currentLoginDate%22%3A%222025-07-22%22%2C%22city%22%3A%22%E9%A6%99%E6%B8%AF%22%2C%22gender%22%3A0%2C%22videoFreeBegin%22%3Anull%2C%22videoFreeEnd%22%3Anull%2C%22actorFreeBegin%22%3Anull%2C%22actorFreeEnd%22%3Anull%2C%22expand%22%3Anull%7D%2C%22searchList%22%3A%5B%5D%2C%22scrollLeft%22%3A0%2C%22dialogTime%22%3A1753149166786%2C%22dialog4Time%22%3A0%2C%22dialog9Time%22%3A1753149166786%2C%22dialog16Time%22%3A0%2C%22agentCode%22%3A%22TDV6MC4J%22%2C%22inviteCode%22%3A%22%22%7D',
            'token': 'c10b205604144861a63ae042369462b9.XcUp61ZDpRMflkP274FoxYmcz8D2kaaoqyiL1dzNmPYUd4aEwsXUZc%2BY5hTxYN7mfBY9ol05xRT3zvt4vradox5F4IOs748CbYjpA%2FhxX%2BmT40LZqxObs6f%2FHCAGvGKXS2DOqCYKkXc0zw4wXiO%2B9506ab0iJKQl.00b4680784e9fffacbbc6c8a8eab820c',
        }
        
        # 代理设置 - 使用Clash代理（与软件端保持一致）
        proxies = {
            'http': 'http://127.0.0.1:7890',
            'https': 'http://127.0.0.1:7890'
        }
        
        # 检查是否需要停止
        if task_id and task_stop_flags.get(task_id, False):
            if log_callback:
                log_callback("检测到停止信号，终止页面下载")
            return 0
        
        # 获取页面内容
        response = requests.get(page_url, headers=headers, cookies=cookies, proxies=proxies, verify=False, timeout=30)
        if response.status_code != 200:
            if log_callback:
                log_callback(f"页面请求失败，状态码: {response.status_code}")
            return 0
        
        page_content = response.text
        
        # 尝试多种图片链接模式
        patterns = [
            r'https://d1y6809ipmcbzp\.cloudfront\.net/image[^\s"\'<>\\]*',  # CloudFront
            r'https://[^/]*\.cloudfront\.net/[^\s"\'<>\\]*\.(jpg|jpeg|png|gif|webp)',  # 其他CloudFront
            r'https://[^/]*\.amazonaws\.com/[^\s"\'<>\\]*\.(jpg|jpeg|png|gif|webp)',  # AWS S3
            r'https://[^/]*wmqxz\.net/[^\s"\'<>\\]*\.(jpg|jpeg|png|gif|webp)',  # 本站图片
            r'src=["\']([^"\']*\.(jpg|jpeg|png|gif|webp))["\']',  # img标签src
        ]
        
        found_urls = []
        for pattern in patterns:
            matches = re.findall(pattern, page_content, re.IGNORECASE)
            if isinstance(matches[0] if matches else None, tuple):
                # 处理带分组的正则结果
                found_urls.extend([match[0] for match in matches])
            else:
                found_urls.extend(matches)
        
        # 清理URL
        cleaned_urls = []
        for url in found_urls:
            cleaned_url = url.rstrip('\\"\',;')
            if cleaned_url and cleaned_url.startswith('http') and cleaned_url not in cleaned_urls:
                cleaned_urls.append(cleaned_url)
        
        unique_urls = list(set(cleaned_urls))
        
        if log_callback:
            log_callback(f"找到 {len(unique_urls)} 个图片链接")
        
        if not unique_urls:
            if log_callback:
                # 调试信息：显示页面内容的一小部分
                preview = page_content[:500].replace('\n', ' ')
                log_callback(f"未找到图片链接，页面预览: {preview}...")
            return 0
        
        # 提取页面标题和作者信息（与软件端保持一致）
        title = "未知标题"
        author = "未知作者"
        
        try:
            # 提取页面标题
            title_match = re.findall(r'<title>(.*?)</title>', page_content, re.IGNORECASE)
            if title_match:
                title = title_match[0].strip()
                # 清理标题中的特殊字符，用于文件夹名
                title = re.sub(r'[<>:"/\\|?*]', '_', title)
            
            # 尝试提取作者信息
            author_patterns = [
                r'作者[：:]\s*([^<>\n]+)',
                r'author[：:]\s*([^<>\n]+)',
                r'by\s+([^<>\n]+)',
                r'发布者[：:]\s*([^<>\n]+)',
                r'用户名[：:]\s*([^<>\n]+)',
                r'"author"[：:]\s*"([^"]+)"',
                r'class="author"[^>]*>([^<]+)',
            ]
            
            for pattern in author_patterns:
                author_match = re.search(pattern, page_content, re.IGNORECASE)
                if author_match:
                    author = author_match.group(1).strip()
                    # 清理作者名中的特殊字符
                    author = re.sub(r'[<>:"/\\|?*]', '_', author)
                    break
        except Exception as e:
            if log_callback:
                log_callback(f"提取页面信息时出错: {str(e)}")
        
        # 创建页面子目录 - 优先使用作者名，备用使用标题（与软件端保持一致）
        folder_name = author if author != "未知作者" else title
        # 更严格的文件名清理，移除所有可能导致路径问题的字符
        folder_name = re.sub(r'[<>:"/\\|?*\'\"()[\]{}~`!@#$%^&+=;,]', '_', folder_name)
        # 移除多余的空格和下划线
        folder_name = re.sub(r'_+', '_', folder_name).strip('_')[:50]
        # 确保文件夹名不为空
        if not folder_name or folder_name == '_':
            import time
            folder_name = f"page_{int(time.time())}"
        
        # 使用正确的路径分隔符
        page_dir = os.path.normpath(os.path.join(save_dir, folder_name))
        
        if log_callback:
            log_callback(f"页面标题: {title}")
            log_callback(f"页面作者: {author}")
            log_callback(f"文件夹名: {folder_name}")
            log_callback(f"完整路径: {page_dir}")
        
        # 创建目录，添加错误处理
        try:
            os.makedirs(page_dir, exist_ok=True)
            if log_callback:
                log_callback(f"✅ 成功创建目录: {page_dir}")
        except Exception as e:
            if log_callback:
                log_callback(f"❌ 创建目录失败: {str(e)}")
            return 0
        
        successful = 0
        
        def download_image(img_url, index):
            try:
                download_headers = headers.copy()
                download_headers.update({
                    "Referer": "https://wmqxz.net/",
                    "Accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"
                })
                
                img_response = requests.get(img_url, headers=download_headers, proxies=proxies, timeout=30)
                if img_response.status_code == 200:
                    url_parts = img_url.split('/')
                    filename = url_parts[-1] if url_parts else f"image_{index+1}.jpg"
                    
                    # 清理文件名，移除可能导致问题的字符
                    filename = re.sub(r'[<>:"/\\|?*\'\"()[\]{}~`!@#$%^&+=;,]', '_', filename)
                    filename = re.sub(r'_+', '_', filename).strip('_')
                    
                    if not any(filename.lower().endswith(ext) for ext in ['.jpg', '.jpeg', '.png', '.gif', '.webp']):
                        filename += '.jpg'
                    
                    # 确保文件名不为空
                    if not filename or filename == '_':
                        filename = f"image_{index+1}.jpg"
                    
                    # 使用正确的路径分隔符
                    filepath = os.path.normpath(os.path.join(page_dir, filename))
                    
                    # 确保目录存在
                    os.makedirs(os.path.dirname(filepath), exist_ok=True)
                    
                    with open(filepath, 'wb') as f:
                        f.write(img_response.content)
                    
                    return True, None
                else:
                    return False, f"HTTP {img_response.status_code}"
            except Exception as e:
                return False, str(e)
        
        # 多线程下载 - 按照软件端配置使用5个线程
        failed = 0
        error_details = []
        
        with ThreadPoolExecutor(max_workers=5) as executor:
            futures = [executor.submit(download_image, url, i) for i, url in enumerate(unique_urls)]
            for i, future in enumerate(as_completed(futures)):
                try:
                    success, error = future.result()
                    if success:
                        successful += 1
                    else:
                        failed += 1
                        if error and len(error_details) < 3:  # 只记录前3个错误
                            error_details.append(f"图片{i+1}: {error}")
                except Exception as e:
                    failed += 1
                    if len(error_details) < 3:
                        error_details.append(f"图片{i+1}: {str(e)}")
        
        # 详细的下载结果日志
        if log_callback:
            if successful > 0:
                log_callback(f"✅ 成功下载 {successful} 张图片")
            if failed > 0:
                log_callback(f"❌ 失败 {failed} 张图片")
                for error in error_details:
                    log_callback(f"  - {error}")
        
        return successful
        
    except Exception as e:
        return 0

# 全局事件循环引用
main_loop = None

# 设置manager的函数
def set_manager(websocket_manager):
    global manager
    manager = websocket_manager

def set_main_loop(loop):
    """设置主事件循环"""
    global main_loop
    main_loop = loop

def safe_send_message(client_id, message):
    """线程安全发送消息"""
    try:
        # 尝试获取当前运行的事件循环
        try:
            loop = asyncio.get_running_loop()
        except RuntimeError:
            # 如果没有运行中的循环，使用全局循环
            loop = main_loop
        
        if loop and not loop.is_closed():
            future = asyncio.run_coroutine_threadsafe(
                manager.send_message(client_id, message), 
                loop
            )
            # 等待一小段时间确保消息发送
            try:
                future.result(timeout=1.0)
            except:
                pass  # 忽略超时错误
            # 静默处理消息发送
            pass
        else:
            pass  # 静默处理事件循环不可用
    except Exception:
        pass  # 静默处理发送消息失败

@router.post("/download/douyin-video")
async def download_douyin_video(request: DownloadRequest):
    """抖音视频下载"""
    try:
        os.makedirs(request.save_path, exist_ok=True)
        
        def progress_callback(progress):
            safe_send_message(request.client_id, {
                "type": "progress",
                "progress": progress,
                "task_type": "douyin"
            })

        def log_callback(message):
            safe_send_message(request.client_id, {
                "type": "log",
                "message": message,
                "task_type": "douyin"
            })
        
        def download_task():
            try:
                # 测试消息发送
                log_callback("开始抖音视频下载...")
                progress_callback(10)
                
                downloader_core.get_video_url_and_download(
                    request.url, 
                    request.save_path, 
                    progress_callback, 
                    log_callback
                )
                safe_send_message(request.client_id, {
                    "type": "complete",
                    "message": "抖音视频下载完成",
                    "task_type": "douyin"
                })
            except Exception as e:
                safe_send_message(request.client_id, {
                    "type": "error",
                    "message": str(e),
                    "task_type": "douyin"
                })
        
        thread = threading.Thread(target=download_task)
        thread.start()
        
        return {"status": "started", "message": "抖音视频下载任务已开始"}
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/download/douyin-image")
async def download_douyin_image(request: DownloadRequest):
    """抖音图片下载"""
    try:
        os.makedirs(request.save_path, exist_ok=True)
        
        def progress_callback(progress):
            safe_send_message(request.client_id, {
                "type": "progress",
                "progress": progress,
                "task_type": "douyin"
            })

        def log_callback(message):
            safe_send_message(request.client_id, {
                "type": "log",
                "message": message,
                "task_type": "douyin"
            })
        
        def download_task():
            try:
                # 测试消息发送
                log_callback("开始抖音图片下载...")
                progress_callback(5)
                
                downloader_core.get_douyin_img(
                    request.url, 
                    request.save_path, 
                    progress_callback, 
                    log_callback
                )
                safe_send_message(request.client_id, {
                    "type": "complete",
                    "message": "抖音图片下载完成",
                    "task_type": "douyin"
                })
            except Exception as e:
                safe_send_message(request.client_id, {
                    "type": "error",
                    "message": str(e),
                    "task_type": "douyin"
                })
        
        thread = threading.Thread(target=download_task)
        thread.start()
        
        return {"status": "started", "message": "抖音图片下载任务已开始"}
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/download/kuaishou")
async def download_kuaishou_video(request: DownloadRequest):
    """快手视频下载"""
    try:
        os.makedirs(request.save_path, exist_ok=True)
        
        def progress_callback(progress):
            safe_send_message(request.client_id, {
                "type": "progress",
                "progress": progress,
                "task_type": "kuaishou"
            })

        def log_callback(message):
            safe_send_message(request.client_id, {
                "type": "log",
                "message": message,
                "task_type": "kuaishou"
            })
        
        def download_task():
            try:
                # 测试消息发送
                log_callback("开始快手视频下载...")
                progress_callback(5)
                
                downloader_core.download_kuaishou_video(
                    request.url, 
                    request.save_path, 
                    progress_callback, 
                    log_callback
                )
                safe_send_message(request.client_id, {
                    "type": "complete",
                    "message": "快手视频下载完成",
                    "task_type": "kuaishou"
                })
            except Exception as e:
                safe_send_message(request.client_id, {
                    "type": "error",
                    "message": str(e),
                    "task_type": "kuaishou"
                })
        
        thread = threading.Thread(target=download_task)
        thread.start()
        
        return {"status": "started", "message": "快手下载任务已开始"}
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/download/kuaishou-batch")
async def download_kuaishou_batch(request: KuaishouBatchRequest):
    """快手批量下载"""
    try:
        os.makedirs(request.save_path, exist_ok=True)

        def progress_callback(progress):
            safe_send_message(request.client_id, {
                "type": "progress",
                "progress": progress,
                "task_type": "kuaishou_batch"
            })

        def log_callback(message):
            safe_send_message(request.client_id, {
                "type": "log",
                "message": message,
                "task_type": "kuaishou_batch"
            })

        def download_task():
            try:
                log_callback(f"开始快手批量下载任务 - 用户ID: {request.user_id}")
                progress_callback(5)

                # 直接实现快手批量下载逻辑，不依赖桌面端模块
                try:
                    # 创建快手批量下载器实例
                    downloader = KuaishouBatchDownloader(request.user_id, request.save_path)

                    # 设置回调函数
                    def handle_log(message):
                        log_callback(message)

                    def handle_progress(progress):
                        progress_callback(progress)

                    def handle_finished():
                        safe_send_message(request.client_id, {
                            "type": "complete",
                            "message": "快手批量下载完成",
                            "task_type": "kuaishou_batch"
                        })

                    def handle_error(error_msg):
                        safe_send_message(request.client_id, {
                            "type": "error",
                            "message": error_msg,
                            "task_type": "kuaishou_batch"
                        })

                    # 执行下载
                    success = downloader.download_all(
                        log_callback=handle_log,
                        progress_callback=handle_progress,
                        stop_flag_callback=lambda: request.task_id and task_stop_flags.get(request.task_id, False)
                    )

                    if success:
                        handle_finished()
                    else:
                        handle_error("快手批量下载失败")

                except Exception as e:
                    log_callback(f"❌ 快手批量下载失败: {str(e)}")
                    safe_send_message(request.client_id, {
                        "type": "error",
                        "message": str(e),
                        "task_type": "kuaishou_batch"
                    })

            except Exception as e:
                log_callback(f"❌ 快手批量下载失败: {str(e)}")
                safe_send_message(request.client_id, {
                    "type": "error",
                    "message": str(e),
                    "task_type": "kuaishou_batch"
                })

        # 存储任务ID用于停止功能
        if request.task_id:
            active_tasks[request.task_id] = threading.Thread(target=download_task)
            task_stop_flags[request.task_id] = False
            active_tasks[request.task_id].start()
        else:
            thread = threading.Thread(target=download_task)
            thread.start()

        return {"status": "started", "message": f"快手批量下载任务已开始 - 用户ID: {request.user_id}"}

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/download/bilibili")
async def download_bilibili_video(request: DownloadRequest):
    """B站视频下载"""
    try:
        os.makedirs(request.save_path, exist_ok=True)

        def progress_callback(progress):
            safe_send_message(request.client_id, {
                "type": "progress",
                "progress": progress,
                "task_type": "bilibili"
            })

        def log_callback(message):
            safe_send_message(request.client_id, {
                "type": "log",
                "message": message,
                "task_type": "bilibili"
            })

        def download_task():
            try:
                # 测试消息发送
                log_callback("开始B站视频下载...")
                progress_callback(5)

                downloader_core.download_bilibili_video(
                    request.url,
                    request.save_path,
                    progress_callback,
                    log_callback
                )
                safe_send_message(request.client_id, {
                    "type": "complete",
                    "message": "B站视频下载完成",
                    "task_type": "bilibili"
                })
            except Exception as e:
                safe_send_message(request.client_id, {
                    "type": "error",
                    "message": str(e),
                    "task_type": "bilibili"
                })

        thread = threading.Thread(target=download_task)
        thread.start()

        return {"status": "started", "message": "B站下载任务已开始"}

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/download/vmq")
async def download_vmq(request: DownloadRequest):
    """微密圈下载"""
    try:
        os.makedirs(request.save_path, exist_ok=True)
        
        def progress_callback(progress):
            safe_send_message(request.client_id, {
                "type": "progress",
                "progress": progress
            })
        
        def log_callback(message):
            safe_send_message(request.client_id, {
                "type": "log",
                "message": message
            })
        
        def download_task():
            try:
                log_callback("开始解析微密圈链接...")
                
                # 检查链接是否为微密圈链接
                if "wmqxz.net" in request.url:
                    # 手动实现微密圈下载逻辑（不依赖Qt）
                    import requests
                    import re
                    from pathlib import Path
                    import time
                    from concurrent.futures import ThreadPoolExecutor, as_completed
                    
                    headers = {
                        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
                        "Accept-Language": "zh-CN,zh;q=0.9,ja;q=0.8",
                        "Cache-Control": "max-age=0",
                        "Connection": "keep-alive",
                        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
                    }
                    
                    # 代理设置 - 使用Clash代理（与软件端保持一致）
                    proxies = {
                        'http': 'http://127.0.0.1:7890',
                        'https': 'http://127.0.0.1:7890'
                    }
                    
                    # 获取页面内容
                    log_callback("正在获取页面内容...")
                    response = requests.get(request.url, headers=headers, proxies=proxies, verify=False, timeout=30)
                    
                    if response.status_code == 200:
                        page_content = response.text
                        
                        # 提取页面标题
                        title_match = re.search(r'<title>(.*?)</title>', page_content, re.IGNORECASE)
                        title = "微密圈下载"
                        if title_match:
                            title = title_match.group(1).strip()
                            title = re.sub(r'[<>:"/\\|?*]', '_', title)[:100]
                        
                        log_callback(f"页面标题: {title}")
                        
                        # 搜索CloudFront图片URL
                        log_callback("正在搜索图片链接...")
                        target_prefix = "https://d1y6809ipmcbzp.cloudfront.net/image"
                        pattern = r'https://d1y6809ipmcbzp\.cloudfront\.net/image[^\s"\'<>\\]*'
                        found_urls = re.findall(pattern, page_content)
                        
                        # 清理URL
                        cleaned_urls = []
                        for url in found_urls:
                            cleaned_url = url.rstrip('\\"\',;')
                            if cleaned_url and cleaned_url not in cleaned_urls:
                                cleaned_urls.append(cleaned_url)
                        
                        unique_urls = list(set(cleaned_urls))
                        
                        if unique_urls:
                            log_callback(f"找到 {len(unique_urls)} 个图片链接")
                            
                            # 创建下载目录
                            final_download_dir = os.path.join(request.save_path, title)
                            os.makedirs(final_download_dir, exist_ok=True)
                            log_callback(f"创建文件夹: {final_download_dir}")
                            
                            # 开始多线程下载图片
                            log_callback("开始多线程下载图片...")
                            successful = 0
                            failed = 0
                            total_imgs = len(unique_urls)
                            completed = 0
                            
                            import threading
                            lock = threading.Lock()
                            
                            def download_vmq_image(img_url, index):
                                try:
                                    download_headers = headers.copy()
                                    download_headers.update({
                                        "Referer": "https://wmqxz.net/",
                                        "Accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"
                                    })
                                    
                                    img_response = requests.get(img_url, headers=download_headers, proxies=proxies, timeout=30, stream=True)
                                    if img_response.status_code == 200:
                                        # 从URL提取文件名
                                        url_parts = img_url.split('/')
                                        filename = url_parts[-1] if url_parts else f"image_{index+1}"
                                        
                                        # 确保文件名有扩展名
                                        if not any(filename.lower().endswith(ext) for ext in ['.jpg', '.jpeg', '.png', '.gif', '.webp']):
                                            filename += '.jpg'
                                        
                                        filepath = os.path.join(final_download_dir, filename)
                                        
                                        with open(filepath, 'wb') as f:
                                            for chunk in img_response.iter_content(chunk_size=8192):
                                                if chunk:
                                                    f.write(chunk)
                                        
                                        return True, filename, None
                                    else:
                                        return False, f"image_{index+1}.jpg", f"状态码: {img_response.status_code}"
                                except Exception as e:
                                    return False, f"image_{index+1}.jpg", str(e)
                            
                            # 使用线程池下载
                            max_workers = min(3, total_imgs)
                            with ThreadPoolExecutor(max_workers=max_workers) as executor:
                                future_to_info = {
                                    executor.submit(download_vmq_image, img_url, i): (img_url, i)
                                    for i, img_url in enumerate(unique_urls)
                                }
                                
                                for future in as_completed(future_to_info):
                                    img_url, index = future_to_info[future]
                                    try:
                                        success, filename, error = future.result()
                                        
                                        with lock:
                                            completed += 1
                                            if success:
                                                successful += 1
                                                log_callback(f"✅ [{completed}/{total_imgs}] {filename}")
                                            else:
                                                failed += 1
                                                log_callback(f"❌ [{completed}/{total_imgs}] {filename} - {error}")
                                            
                                            # 更新进度
                                            progress = (completed / total_imgs) * 100
                                            progress_callback(progress)
                                            
                                    except Exception as e:
                                        with lock:
                                            completed += 1
                                            failed += 1
                                            log_callback(f"❌ [{completed}/{total_imgs}] 下载异常: {str(e)}")
                                            progress = (completed / total_imgs) * 100
                                            progress_callback(progress)
                            
                            progress_callback(100)
                            
                            if successful > 0:
                                log_callback(f"✅ 微密圈下载完成！成功：{successful}张，失败：{failed}张")
                                safe_send_message(request.client_id, {
                                    "type": "complete",
                                    "message": f"微密圈下载完成 - 成功：{successful}张，失败：{failed}张"
                                })
                            else:
                                log_callback("❌ 所有图片下载失败")
                                safe_send_message(request.client_id, {
                                    "type": "error",
                                    "message": "所有图片下载失败"
                                })
                        else:
                            log_callback("❌ 未找到图片链接")
                            safe_send_message(request.client_id, {
                                "type": "error",
                                "message": "未找到图片链接"
                            })
                    else:
                        log_callback(f"❌ 获取页面失败，状态码: {response.status_code}")
                        safe_send_message(request.client_id, {
                            "type": "error",
                            "message": f"获取页面失败，状态码: {response.status_code}"
                        })
                else:
                    # 铁粉圈链接处理
                    log_callback("检测到铁粉圈链接...")
                    
                    try:
                        # 手动实现铁粉圈下载逻辑（不依赖Qt信号）
                        import requests
                        from bs4 import BeautifulSoup
                        import re
                        from pathlib import Path
                        import time
                        
                        headers = {
                            "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
                            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
                            "cache-control": "max-age=0",
                            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36"
                        }
                        cookies = {"timezone": "8"}
                        
                        log_callback("🌐 开始获取网页内容...")
                        response = requests.get(request.url, headers=headers, cookies=cookies, timeout=30)
                        
                        if response.status_code != 200:
                            error_msg = f"❌ 获取网页失败，状态码: {response.status_code}"
                            log_callback(error_msg)
                            safe_send_message(request.client_id, {
                                "type": "error",
                                "message": "网页获取失败"
                            })
                            return
                        
                        log_callback("📄 解析网页内容...")
                        soup = BeautifulSoup(response.text, 'html.parser')
                        target_div = soup.find('div', class_='post-content tiefenquan', id='lightgallery')
                        
                        img_srcs = []
                        if target_div:
                            img_tags = target_div.find_all('img')
                            img_srcs = [img['src'] for img in img_tags if img.get('src')]
                            log_callback(f"🔍 找到 {len(img_srcs)} 张图片")
                        else:
                            error_msg = "❌ 未找到目标内容"
                            log_callback(error_msg)
                            safe_send_message(request.client_id, {
                                "type": "error",
                                "message": "未找到图片内容"
                            })
                            return
                        
                        if not img_srcs:
                            log_callback("❌ 没有找到可下载的图片")
                            safe_send_message(request.client_id, {
                                "type": "error",
                                "message": "没有找到可下载的图片"
                            })
                            return
                        
                        # 获取标题作为文件夹名
                        title_tag = soup.find('title')
                        if title_tag:
                            title = title_tag.get_text().strip()
                            title = re.sub(r'[\\/*?:"<>|]', "", title)
                            title = title[:100]  # 限制长度
                        else:
                            title = f"铁粉圈下载_{int(time.time())}"
                        
                        folder_path = Path(request.save_path) / title
                        folder_path.mkdir(parents=True, exist_ok=True)
                        log_callback(f"📁 创建文件夹: {folder_path}")
                        
                        # 开始下载图片（多线程）
                        log_callback("⬇️ 开始多线程下载图片...")
                        successful = 0
                        failed = 0
                        total_imgs = len(img_srcs)
                        completed = 0
                        
                        # 线程安全的计数器
                        import threading
                        from concurrent.futures import ThreadPoolExecutor, as_completed
                        
                        lock = threading.Lock()
                        
                        def download_single_image(img_url, index):
                            try:
                                img_response = requests.get(img_url, headers=headers, timeout=30)
                                if img_response.status_code == 200:
                                    img_name = f"image_{index+1:03d}.jpg"
                                    img_path = folder_path / img_name
                                    
                                    with open(img_path, 'wb') as f:
                                        f.write(img_response.content)
                                    
                                    return True, img_name, None
                                else:
                                    return False, f"image_{index+1:03d}.jpg", f"状态码: {img_response.status_code}"
                            except Exception as e:
                                return False, f"image_{index+1:03d}.jpg", str(e)
                        
                        # 使用线程池下载
                        max_workers = min(5, total_imgs)  # 最多5个线程
                        with ThreadPoolExecutor(max_workers=max_workers) as executor:
                            # 提交所有下载任务
                            future_to_info = {
                                executor.submit(download_single_image, img_url, i): (img_url, i)
                                for i, img_url in enumerate(img_srcs)
                            }
                            
                            # 处理完成的任务
                            for future in as_completed(future_to_info):
                                img_url, index = future_to_info[future]
                                try:
                                    success, img_name, error = future.result()
                                    
                                    with lock:
                                        completed += 1
                                        if success:
                                            successful += 1
                                            log_callback(f"✅ [{completed}/{total_imgs}] {img_name}")
                                        else:
                                            failed += 1
                                            log_callback(f"❌ [{completed}/{total_imgs}] {img_name} - {error}")
                                        
                                        # 更新进度
                                        progress = (completed / total_imgs) * 100
                                        progress_callback(progress)
                                        
                                except Exception as e:
                                    with lock:
                                        completed += 1
                                        failed += 1
                                        log_callback(f"❌ [{completed}/{total_imgs}] 下载异常: {str(e)}")
                                        progress = (completed / total_imgs) * 100
                                        progress_callback(progress)
                        
                        progress_callback(100)
                        
                        if successful > 0:
                            log_callback(f"✅ 铁粉圈下载完成！成功：{successful}张，失败：{failed}张")
                            safe_send_message(request.client_id, {
                    "type": "complete",
                                "message": f"铁粉圈下载完成 - 成功：{successful}张，失败：{failed}张"
                            })
                        else:
                            log_callback("❌ 所有图片下载失败")
                            safe_send_message(request.client_id, {
                                "type": "error",
                                "message": "所有图片下载失败"
                            })
                            
                    except Exception as e:
                        log_callback(f"铁粉圈下载失败: {str(e)}")
                        safe_send_message(request.client_id, {
                            "type": "error",
                            "message": f"铁粉圈下载失败: {str(e)}"
                        })
                        
            except Exception as e:
                safe_send_message(request.client_id, {
                    "type": "error",
                    "message": str(e)
                })
        
        thread = threading.Thread(target=download_task)
        thread.start()
        
        return {"status": "started", "message": "微密圈下载任务已开始"}
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/download/vmq-batch")
async def download_vmq_batch(request: dict):
    """微密圈批量下载"""
    try:
        category = request.get('category', '')
        page = request.get('page', 1)
        save_path = request.get('save_path', './downloads')
        client_id = request.get('client_id', 'default')
        
        os.makedirs(save_path, exist_ok=True)
        
        def progress_callback(progress):
            safe_send_message(client_id, {
                "type": "progress",
                "progress": progress
            })
        
        def log_callback(message):
            safe_send_message(client_id, {
                "type": "log",
                "message": message
            })
        
        def download_task():
            try:
                # 使用vmq_batch模块的功能
                log_callback(f"开始爬取 {category} 分类，第 {page} 页")
                
                # 可用分类验证
                available_categories = ["jvid", "xiuren", "weimimao", "fuliji", "mote", "sentian", "hanfu"]
                if category not in available_categories:
                    log_callback(f"不支持的分类 '{category}'，可用分类: {', '.join(available_categories)}")
                    safe_send_message(client_id, {
                        "type": "error",
                        "message": f"不支持的分类 '{category}'"
                    })
                    return
                
                # 实际的批量下载逻辑
                log_callback("正在获取分类页面...")
                
                try:
                    # 导入批量下载模块
                    import sys
                    from pathlib import Path
                    sys.path.insert(0, str(Path(__file__).parent.parent.parent))
                    
                    from vmq_batch import get_vmq_data, CATEGORY_IDS
                    
                    # 设置保存路径
                    category_path = os.path.join(save_path, f"{category}_第{page}页")
                    os.makedirs(category_path, exist_ok=True)
                    
                    log_callback(f"创建分类文件夹: {category_path}")
                    
                    # 获取VMQ数据
                    log_callback("正在获取页面数据...")
                    progress_callback(20)
                    
                    try:
                        # 调用VMQ批量获取函数
                        api_data = get_vmq_data(page_num=page, page_size=20, category_suffix=category, silent=True)
                        
                        if api_data:
                            log_callback("获取API数据成功，正在提取页面信息...")
                            progress_callback(40)
                            
                            # 导入提取函数
                            from vmq_batch import extract_posts_from_response
                            
                            # 提取页面信息
                            posts = extract_posts_from_response(api_data, silent=True)
                            
                            if posts:
                                log_callback(f"获取到 {len(posts)} 个页面")
                                progress_callback(50)
                                
                                successful_pages = 0
                                failed_pages = 0
                                total_images = 0
                                
                                # 批量下载每个页面 - 使用多线程并发处理（按照软件端配置）
                                from concurrent.futures import ThreadPoolExecutor, as_completed
                                import threading
                                
                                # 线程安全的计数器
                                lock = threading.Lock()
                                completed_count = 0
                                
                                def download_page(post_info):
                                    try:
                                        i, post = post_info
                                        task_id = request.get('task_id')
                                        
                                        # 检查是否需要停止
                                        if task_id and task_stop_flags.get(task_id, False):
                                            return i, 0, "停止信号"
                                        
                                        page_url = post['url']
                                        page_title = post['title']
                                        log_callback(f"正在处理第 {i+1}/{len(posts)} 个页面: {page_title[:30]}...")
                                        
                                        # 下载单个页面的图片
                                        page_images = download_single_vmq_page(page_url, category_path, log_callback, task_id)
                                        
                                        return i, page_images, None
                                        
                                    except Exception as e:
                                        return i, 0, str(e)
                                
                                # 使用2个线程并发处理页面（与软件端保持一致）
                                log_callback("🚀 开始并发下载，使用2个线程处理页面，每页面内使用5个线程下载图片")
                                
                                with ThreadPoolExecutor(max_workers=2) as executor:
                                    # 提交所有页面下载任务
                                    future_to_post = {
                                        executor.submit(download_page, (i, post)): (i, post) 
                                        for i, post in enumerate(posts)
                                    }
                                    
                                    # 处理完成的任务
                                    for future in as_completed(future_to_post):
                                        try:
                                            i, page_images, error = future.result()
                                            
                                            with lock:
                                                completed_count += 1
                                                
                                                if error:
                                                    if error == "停止信号":
                                                        log_callback("检测到停止信号，终止批量下载")
                                                        break
                                                    else:
                                                        failed_pages += 1
                                                        log_callback(f"❌ 页面 {i+1} 处理失败: {error}")
                                                elif page_images > 0:
                                                    successful_pages += 1
                                                    total_images += page_images
                                                    log_callback(f"✅ 页面 {i+1} 完成，下载 {page_images} 张图片")
                                                else:
                                                    failed_pages += 1
                                                    log_callback(f"❌ 页面 {i+1} 未找到图片")
                                                
                                                # 更新进度
                                                progress = 50 + int(completed_count / len(posts) * 40)
                                                progress_callback(progress)
                                                
                                        except Exception as e:
                                            with lock:
                                                completed_count += 1
                                                failed_pages += 1
                                                log_callback(f"❌ 任务执行异常: {str(e)}")
                                                
                                                # 更新进度
                                                progress = 50 + int(completed_count / len(posts) * 40)
                                                progress_callback(progress)
                                
                                progress_callback(100)
                                
                                if successful_pages > 0:
                                    log_callback(f"✅ 批量下载完成！")
                                    log_callback(f"成功页面: {successful_pages}，失败页面: {failed_pages}")
                                    log_callback(f"总计下载图片: {total_images} 张")
                                    safe_send_message(client_id, {
                    "type": "complete",
                                        "message": f"批量下载完成 - 成功{successful_pages}页，共{total_images}张图片"
                                    })
                                else:
                                    log_callback("❌ 所有页面下载失败")
                                    safe_send_message(client_id, {
                                        "type": "error",
                                        "message": "所有页面下载失败"
                                    })
                            else:
                                log_callback("❌ 未能从API响应中提取到页面信息")
                                safe_send_message(client_id, {
                                    "type": "error",
                                    "message": "未能提取到页面信息"
                                })
                        else:
                            log_callback("❌ 未获取到API数据")
                            safe_send_message(client_id, {
                                "type": "error",
                                "message": "未获取到API数据"
                            })
                            
                    except Exception as batch_error:
                        log_callback(f"❌ 批量获取失败: {str(batch_error)}")
                        safe_send_message(client_id, {
                            "type": "error",
                            "message": f"批量获取失败: {str(batch_error)}"
                        })
                    
                except ImportError as e:
                    log_callback(f"❌ 无法导入批量下载模块: {str(e)}")
                    log_callback("批量下载功能暂时不可用，请联系开发者")
                    safe_send_message(client_id, {
                        "type": "error",
                        "message": "批量下载功能暂时不可用"
                    })
                    
            except Exception as e:
                log_callback(f"❌ 批量下载失败: {str(e)}")
                safe_send_message(client_id, {
                    "type": "error",
                    "message": str(e)
                })
        
        thread = threading.Thread(target=download_task)
        thread.start()
        
        return {"status": "started", "message": f"微密圈批量下载任务已开始 - {category} 第{page}页"}
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/cookies/update")
async def update_cookies(request: CookieRequest):
    """更新Cookie"""
    try:
        count = downloader_core.parse_and_set_cookies(request.cookies, request.platform)
        return {
            "status": "success",
            "message": f"成功更新{count}个{request.platform}Cookie"
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/stop-download")
async def stop_download(request: dict):
    """停止下载任务"""
    try:
        task_id = request.get('task_id')
        client_id = request.get('client_id')
        
        if not task_id:
            raise HTTPException(status_code=400, detail="缺少任务ID")
        
        # 设置停止标志
        task_stop_flags[task_id] = True
        
        # 通知客户端停止成功
        safe_send_message(client_id, {
            "type": "log",
            "message": f"下载任务已请求停止"
        })
        
        return {"message": "停止下载请求已发送"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"停止下载失败: {str(e)}")

@router.post("/password/verify")
async def verify_password(request: dict):
    """验证付费区密码"""
    try:
        password = request.get('password', '')
        client_id = request.get('client_id', 'default')
        
        if not password:
            return {
                "success": False,
                "message": "密码不能为空"
            }
        
        # 导入密码管理模块
        try:
            from security_manager import get_password
        except ImportError:
            # 如果导入失败，使用默认密码
            stored_password = "520184"
            
            success = password == stored_password
            return {
                "success": success,
                "message": "密码验证成功" if success else "密码验证失败"
            }
        
        # 验证密码
        stored_password = get_password("premium_access") or "520184"
        
        success = password == stored_password
        
        # 通知客户端密码验证结果
        safe_send_message(client_id, {
            "type": "password_verification",
            "success": success,
            "message": "密码验证成功" if success else "密码验证失败"
        })
        
        return {
            "success": success,
            "message": "密码验证成功" if success else "密码验证失败"
        }
        
    except Exception as e:
        return {
            "success": False,
            "message": f"密码验证失败: {str(e)}"
        }

@router.post("/password/change")
async def change_password(request: dict):
    """修改付费区密码"""
    try:
        current_password = request.get('current_password', '')
        new_password = request.get('new_password', '')
        client_id = request.get('client_id', 'default')
        
        if not current_password or not new_password:
            raise HTTPException(status_code=400, detail="当前密码和新密码不能为空")
        
        if len(new_password) < 4:
            raise HTTPException(status_code=400, detail="新密码长度至少为4位")
        
        # 导入密码管理模块
        try:
            from security_manager import get_password, store_password
        except ImportError:
            # 如果导入失败，使用默认密码
            stored_password = "520184"
            
            if current_password != stored_password:
                return {
                    "success": False,
                    "message": "当前密码错误"
                }
            
            # 模拟存储成功
            return {
                "success": True,
                "message": "密码修改成功"
            }
        
        # 验证当前密码
        stored_password = get_password("premium_access") or "520184"
        
        if current_password != stored_password:
            return {
                "success": False,
                "message": "当前密码错误"
            }
        
        # 检查新密码是否与当前密码相同
        if current_password == new_password:
            return {
                "success": False,
                "message": "新密码不能与当前密码相同"
            }
        
        # 存储新密码
        if store_password("premium_access", new_password):
            # 通知客户端密码修改成功
            safe_send_message(client_id, {
                "type": "premium_password_changed",
                "success": True,
                "message": "密码修改成功"
            })
            
            return {
                "success": True,
                "message": "密码修改成功"
            }
        else:
            return {
                "success": False,
                "message": "密码存储失败"
            }
            
    except Exception as e:
        return {
            "success": False,
            "message": f"密码修改失败: {str(e)}"
        }

@router.post("/download/tiefenquan-video")
async def download_tiefenquan_video(request: DownloadRequest):
    """铁粉圈视频下载"""
    try:
        os.makedirs(request.save_path, exist_ok=True)

        def progress_callback(progress):
            safe_send_message(request.client_id, {
                "type": "progress",
                "progress": progress,
                "task_type": "tiefenquan-video"
            })

        def log_callback(message):
            safe_send_message(request.client_id, {
                "type": "log",
                "message": message,
                "task_type": "tiefenquan-video"
            })

        def download_task():
            try:
                log_callback("🚀 开始铁粉圈视频下载...")
                progress_callback(10)

                # 使用内置的铁粉圈视频下载实现
                log_callback("🌐 开始获取网页内容...")
                progress_callback(20)

                import requests
                from bs4 import BeautifulSoup
                import re
                from pathlib import Path
                import time

                headers = {
                    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0"
                }
                cookies = {"timezone": "8"}

                response = requests.get(request.url, headers=headers, cookies=cookies, timeout=30)

                if response.status_code != 200:
                    error_msg = f"❌ 获取网页失败，状态码: {response.status_code}"
                    log_callback(error_msg)
                    safe_send_message(request.client_id, {
                        "type": "error",
                        "message": "网页获取失败"
                    })
                    return

                log_callback("🔍 正在解析页面内容...")
                progress_callback(40)
                html_content = response.text

                # 解析页面，查找DPlayer视频链接
                soup = BeautifulSoup(html_content, 'html.parser')
                scripts = soup.find_all('script')

                mp4_url = None
                for script in scripts:
                    if 'DPlayer' in script.text and 'url:' in script.text:
                        # 使用正则表达式提取 url 的值
                        match = re.search(r'url:\s*["\']([^"\']+)["\']', script.text)
                        if match:
                            mp4_url = match.group(1)
                            log_callback(f"✅ 找到视频链接: {mp4_url}")
                            break

                if not mp4_url:
                    error_msg = "❌ 未找到视频链接"
                    log_callback(error_msg)
                    safe_send_message(request.client_id, {
                        "type": "error",
                        "message": "未找到视频链接"
                    })
                    return

                # 确保保存目录存在
                folder_path = Path(request.save_path)
                folder_path.mkdir(parents=True, exist_ok=True)

                # 从URL中提取文件名
                filename = mp4_url.split('/')[-1]
                if not filename.endswith('.mp4'):
                    filename += '.mp4'

                # 如果文件名为空或无效，使用时间戳
                if not filename or filename == '.mp4':
                    filename = f"tiefenquan_video_{int(time.time())}.mp4"

                filepath = folder_path / filename

                log_callback(f"⬇️ 开始下载视频: {filename}")
                progress_callback(60)

                # 下载视频
                video_response = requests.get(mp4_url, headers=headers, stream=True)
                video_response.raise_for_status()

                total_size = int(video_response.headers.get('content-length', 0))
                downloaded = 0

                with open(filepath, 'wb') as f:
                    for chunk in video_response.iter_content(chunk_size=8192):
                        if chunk:
                            f.write(chunk)
                            downloaded += len(chunk)
                            if total_size > 0:
                                progress = 60 + (downloaded / total_size) * 35
                                progress_callback(int(progress))

                progress_callback(100)
                log_callback(f"✅ 视频下载完成: {filename}")
                safe_send_message(request.client_id, {
                    "type": "complete",
                    "message": f"铁粉圈视频下载完成: {filename}"
                })

            except Exception as e:
                log_callback(f"❌ 铁粉圈视频下载失败: {str(e)}")
                safe_send_message(request.client_id, {
                    "type": "error",
                    "message": f"铁粉圈视频下载失败: {str(e)}"
                })

        thread = threading.Thread(target=download_task)
        thread.start()

        return {"status": "started", "message": "铁粉圈视频下载任务已开始"}

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/password/status")
async def get_password_status():
    """获取密码状态"""
    try:
        from security_manager import get_password
        stored_password = get_password("premium_access")

        return {
            "has_password": stored_password is not None,
            "is_default": stored_password == "520184" if stored_password else True
        }
    except ImportError:
        return {
            "has_password": True,
            "is_default": True
        }